package com.wechat.common.id.core;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 基于Redis的分布式雪花算法ID生成器
 *
 * 特性：
 * 1. 支持分布式环境下的唯一ID生成
 * 2. 通过Redis自动分配workerId，避免冲突
 * 3. 支持多种格式的String ID生成
 * 4. 具备降级机制，Redis不可用时仍可工作
 * 5. 包含心跳机制，保持workerId的活跃状态
 *
 * ID结构：64位
 * +----------+----------+----------+
 * | 时间戳42位 | 机器ID10位 | 序列号12位 |
 * +----------+----------+----------+
 */
@SuppressWarnings("BusyWait")
@Slf4j
@Component
public class IdGenerator {

    // ========== 雪花算法常量配置 ==========

    /**
     * 开始时间戳 (2023-01-01 00:00:00 UTC)
     */
    private static final long START_TIMESTAMP = 1672531200000L;

    /**
     * 各部分位数定义
     */
    private static final long WORKER_ID_BITS = 10L;      // 机器ID位数
    private static final long SEQUENCE_BITS = 12L;       // 序列号位数

    /**
     * 最大值计算
     */
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);    // 1023
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);      // 4095

    /**
     * 位移量计算
     */
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;                    // 12
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;  // 22

    // ========== Redis相关配置 ==========

    private static final String WORKER_ID_COUNTER_KEY = "snowflake:worker_id_counter";
    private static final String WORKER_REGISTRY_PREFIX = "snowflake:worker:";
    private static final int WORKER_ID_EXPIRE_SECONDS = 60;
    private static final int HEARTBEAT_INTERVAL_SECONDS = 30;

    // ========== 编码字符集 ==========

    private static final String BASE36_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    // ========== 依赖注入 ==========

    private final StringRedisTemplate stringRedisTemplate;

    public IdGenerator(final StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Getter
    @Value("${spring.application.name:unknown-app}")
    private String applicationName;

    @Value("${id-generator.enable-redis:true}")
    private boolean enableRedis;

    // ========== 实例变量 ==========

    @Getter
    private volatile long workerId;
    private volatile long sequence = 0L;
    private volatile long lastTimestamp = -1L;

    private final Object lock = new Object();
    private final AtomicBoolean initialized = new AtomicBoolean(false);
    private volatile Thread heartbeatThread;

    // ========== 初始化方法 ==========

    @PostConstruct
    public void init() {
        if (initialized.compareAndSet(false, true)) {
            try {
                this.workerId = generateWorkerId();
                log.info("ID生成器初始化成功 - Application: {}, WorkerId: {}, Redis: {}",
                        applicationName, workerId, enableRedis && stringRedisTemplate != null);

                if (enableRedis && stringRedisTemplate != null) {
                    startHeartbeat();
                }
            } catch (Exception e) {
                log.error("ID生成器初始化失败", e);
                throw new RuntimeException("ID生成器初始化失败", e);
            }
        }
    }

    @PreDestroy
    public void destroy() {
        if (heartbeatThread != null && !heartbeatThread.isInterrupted()) {
            heartbeatThread.interrupt();
            log.info("ID生成器心跳线程已停止");
        }
    }

    // ========== 核心ID生成方法 ==========

    /**
     * 生成Long型雪花ID
     *
     * @return 64位长整型ID
     */
    public long generateId() {
        synchronized (lock) {
            long timestamp = System.currentTimeMillis();

            // 时钟回退检查
            if (timestamp < lastTimestamp) {
                long offset = lastTimestamp - timestamp;
                if (offset <= 5) {
                    // 小于5ms的回退，等待追上
                    try {
                        Thread.sleep(offset << 1);
                        timestamp = System.currentTimeMillis();
                        if (timestamp < lastTimestamp) {
                            throw new RuntimeException("时钟回退超过容忍范围，拒绝生成ID");
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException("等待时钟追赶时被中断");
                    }
                } else {
                    throw new RuntimeException("时钟回退过大: " + offset + "ms，拒绝生成ID");
                }
            }

            // 同一毫秒内序列号递增
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE;
                if (sequence == 0) {
                    // 序列号溢出，等待下一毫秒
                    timestamp = waitNextMillis(lastTimestamp);
                }
            } else {
                // 新的毫秒，序列号重置
                sequence = 0L;
            }

            lastTimestamp = timestamp;

            // 组装ID
            return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
                    | (workerId << WORKER_ID_SHIFT)
                    | sequence;
        }
    }

    // ========== String类型ID生成方法 ==========

    /**
     * 生成字符串形式的雪花ID
     *
     * @return 字符串形式的ID
     */
    public String generateStringId() {
        return String.valueOf(generateId());
    }

    /**
     * 生成带前缀的字符串ID
     *
     * @param prefix 前缀
     * @return 带前缀的字符串ID
     */
    public String generateStringId(String prefix) {
        if (prefix == null || prefix.isEmpty()) {
            return generateStringId();
        }
        return prefix + generateId();
    }

    /**
     * 生成Base36编码的ID（更短，36进制）
     *
     * @return Base36编码的字符串
     */
    public String generateBase36Id() {
        return encodeToBase(generateId(), BASE36_CHARS);
    }

    /**
     * 生成Base62编码的ID（最短，62进制）
     *
     * @return Base62编码的字符串
     */
    public String generateBase62Id() {
        return encodeToBase(generateId(), BASE62_CHARS);
    }

    /**
     * 生成带前缀的Base36 ID
     *
     * @param prefix 前缀
     * @return 带前缀的Base36 ID
     */
    public String generateBase36Id(String prefix) {
        if (prefix == null || prefix.isEmpty()) {
            return generateBase36Id();
        }
        return prefix + generateBase36Id();
    }

    /**
     * 生成带前缀的Base62 ID
     *
     * @param prefix 前缀
     * @return 带前缀的Base62 ID
     */
    public String generateBase62Id(String prefix) {
        if (prefix == null || prefix.isEmpty()) {
            return generateBase62Id();
        }
        return prefix + generateBase62Id();
    }

    /**
     * 根据格式生成ID
     *
     * @param format ID格式
     * @return 格式化的ID字符串
     */
    public String generateFormattedId(IdFormat format) {
        long id = generateId();

        return switch (format) {
            case LONG_STRING -> String.valueOf(id);
            case BASE36 -> encodeToBase(id, BASE36_CHARS);
            case BASE62 -> encodeToBase(id, BASE62_CHARS);
            case PREFIXED_LONG -> applicationName.toUpperCase() + "_" + id;
            case PREFIXED_BASE36 -> applicationName.toUpperCase() + "_" + encodeToBase(id, BASE36_CHARS);
            case PREFIXED_BASE62 -> applicationName.toUpperCase() + "_" + encodeToBase(id, BASE62_CHARS);
            case FORMATTED -> formatWithHyphen(id);
        };
    }

    // ========== 业务专用ID生成方法 ==========

    /**
     * 生成文件ID
     * 格式：FILE_yyyyMMddHHmmss_Base62
     *
     * @return 文件专用ID
     */
    public String generateFileId() {
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        return "FILE_" + timestamp + "_" + generateBase62Id();
    }

    /**
     * 生成用户ID
     * 格式：USER_Base62
     *
     * @return 用户专用ID
     */
    public String generateUserId() {
        return "USER_" + generateBase62Id();
    }

    /**
     * 生成消息ID
     * 格式：MSG_Base62
     *
     * @return 消息专用ID
     */
    public String generateMessageId() {
        return "MSG_" + generateBase62Id();
    }

    /**
     * 生成群组ID
     * 格式：GROUP_Base36
     *
     * @return 群组专用ID
     */
    public String generateGroupId() {
        return "GROUP_" + generateBase36Id();
    }

    /**
     * 生成订单ID
     * 格式：ORDER_yyyyMMdd_Base36
     *
     * @return 订单专用ID
     */
    public String generateOrderId() {
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        return "ORDER_" + date + "_" + generateBase36Id();
    }

    /**
     * 生成会话ID
     * 格式：CHAT_Base62
     *
     * @return 会话专用ID
     */
    public String generateChatId() {
        return "CHAT_" + generateBase62Id();
    }

    // ========== 批量生成方法 ==========

    /**
     * 批量生成ID
     *
     * @param count 生成数量
     * @param format ID格式
     * @return ID列表
     */
    public List<String> generateBatchIds(int count, IdFormat format) {
        if (count <= 0) {
            return new ArrayList<>();
        }

        List<String> ids = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            ids.add(generateFormattedId(format));
        }
        return ids;
    }

    /**
     * 批量生成Long型ID
     *
     * @param count 生成数量
     * @return Long型ID列表
     */
    public List<Long> generateBatchLongIds(int count) {
        if (count <= 0) {
            return new ArrayList<>();
        }

        List<Long> ids = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            ids.add(generateId());
        }
        return ids;
    }

    // ========== ID解析方法 ==========

    /**
     * 解析雪花ID
     *
     * @param id 雪花ID
     * @return ID信息
     */
    public IdInfo parseId(long id) {
        long timestamp = (id >> TIMESTAMP_SHIFT) + START_TIMESTAMP;
        long parsedWorkerId = (id >> WORKER_ID_SHIFT) & MAX_WORKER_ID;
        long sequence = id & MAX_SEQUENCE;

        return new IdInfo(id, timestamp, parsedWorkerId, sequence);
    }

    /**
     * 解析字符串形式的雪花ID
     *
     * @param stringId 字符串ID
     * @return ID信息
     */
    public IdInfo parseStringId(String stringId) {
        if (stringId == null || stringId.isEmpty()) {
            throw new IllegalArgumentException("ID字符串不能为空");
        }

        try {
            // 尝试直接解析
            long id = Long.parseLong(stringId);
            return parseId(id);
        } catch (NumberFormatException e) {
            // 尝试提取数字部分
            String numberPart = extractNumberFromId(stringId);
            if (numberPart != null) {
                long id = Long.parseLong(numberPart);
                return parseId(id);
            }
            throw new IllegalArgumentException("无法解析ID: " + stringId, e);
        }
    }

    // ========== WorkerId生成和管理 ==========

    /**
     * 生成WorkerId
     */
    private long generateWorkerId() {
        if (enableRedis && stringRedisTemplate != null) {
            try {
                return generateWorkerIdFromRedis();
            } catch (Exception e) {
                log.warn("从Redis获取WorkerId失败，使用降级方案", e);
                return generateWorkerIdFallback();
            }
        } else {
            log.info("Redis未启用，使用降级方案生成WorkerId");
            return generateWorkerIdFallback();
        }
    }

    /**
     * 从Redis获取WorkerId
     */
    private long generateWorkerIdFromRedis() throws Exception {
        // 尝试获取可用的WorkerId
        for (int attempt = 0; attempt <= MAX_WORKER_ID; attempt++) {
            // 使用StringRedisTemplate的increment操作替代RAtomicLong
            Long counterValue = stringRedisTemplate.opsForValue().increment(WORKER_ID_COUNTER_KEY);
            if (counterValue == null) {
                counterValue = 1L;
            }

            long candidateId = counterValue & MAX_WORKER_ID;

            if (tryRegisterWorkerId(candidateId)) {
                log.info("从Redis成功获取WorkerId: {}", candidateId);
                return candidateId;
            }
        }

        throw new RuntimeException("无法从Redis获取可用的WorkerId");
    }

    /**
     * 尝试注册WorkerId
     */
    private boolean tryRegisterWorkerId(long workerId) {
        try {
            String workerKey = WORKER_REGISTRY_PREFIX + workerId;
            String workerInfo = buildWorkerInfo();

            // 使用StringRedisTemplate的setIfAbsent替代RBucket的trySet
            Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(
                    workerKey,
                    workerInfo,
                    Duration.ofSeconds(WORKER_ID_EXPIRE_SECONDS)
            );

            return Boolean.TRUE.equals(success);

        } catch (Exception e) {
            log.warn("注册WorkerId失败: {}", workerId, e);
            return false;
        }
    }

    /**
     * 构建Worker信息
     */
    private String buildWorkerInfo() {
        try {
            InetAddress localhost = InetAddress.getLocalHost();
            return String.format("%s:%s:%s:%d",
                    applicationName,
                    localhost.getHostName(),
                    localhost.getHostAddress(),
                    System.currentTimeMillis());
        } catch (Exception e) {
            return String.format("%s:unknown:unknown:%d",
                    applicationName,
                    System.currentTimeMillis());
        }
    }

    /**
     * WorkerId降级方案
     */
    private long generateWorkerIdFallback() {
        try {
            // 尝试使用MAC地址
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);

            if (network != null) {
                byte[] mac = network.getHardwareAddress();
                if (mac != null && mac.length >= 6) {
                    long id = 0;
                    for (int i = 0; i < 6; i++) {
                        id = (id << 8) | (mac[i] & 0xFF);
                    }
                    long workerId = Math.abs(id) & MAX_WORKER_ID;
                    log.info("使用MAC地址生成WorkerId: {}", workerId);
                    return workerId;
                }
            }
        } catch (Exception e) {
            log.warn("获取MAC地址失败", e);
        }

        // 最后的降级方案：随机数
        long workerId = ThreadLocalRandom.current().nextLong(0, MAX_WORKER_ID + 1);
        log.warn("使用随机数生成WorkerId: {}", workerId);
        return workerId;
    }

    // ========== 心跳机制 ==========

    /**
     * 启动心跳线程
     */
    private void startHeartbeat() {
        heartbeatThread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(HEARTBEAT_INTERVAL_SECONDS * 1000);
                    updateWorkerHeartbeat();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.info("心跳线程被中断，退出");
                    break;
                } catch (Exception e) {
                    log.warn("更新WorkerId心跳失败", e);
                }
            }
        }, "snowflake-heartbeat-" + workerId);

        heartbeatThread.setDaemon(true);
        heartbeatThread.start();
        log.info("WorkerId心跳线程启动: {}", workerId);
    }

    /**
     * 更新Worker心跳
     */
    private void updateWorkerHeartbeat() {
        try {
            if (stringRedisTemplate != null) {
                String workerKey = WORKER_REGISTRY_PREFIX + workerId;
                String workerInfo = buildWorkerInfo();

                // 使用StringRedisTemplate的set替代RBucket的set
                stringRedisTemplate.opsForValue().set(
                        workerKey,
                        workerInfo,
                        Duration.ofSeconds(WORKER_ID_EXPIRE_SECONDS)
                );

                log.debug("WorkerId心跳更新成功: {}", workerId);
            }
        } catch (Exception e) {
            log.warn("更新WorkerId心跳失败: {}", workerId, e);
        }
    }

    // ========== 工具方法 ==========

    /**
     * 等待下一毫秒
     */
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 进制编码
     */
    private String encodeToBase(long value, String chars) {
        if (value == 0) {
            return String.valueOf(chars.charAt(0));
        }

        StringBuilder result = new StringBuilder();
        long num = Math.abs(value);
        int base = chars.length();

        while (num > 0) {
            result.insert(0, chars.charAt((int) (num % base)));
            num /= base;
        }

        return result.toString();
    }

    /**
     * 格式化ID（添加连字符）
     */
    private String formatWithHyphen(long id) {
        String idStr = String.valueOf(id);
        StringBuilder result = new StringBuilder();

        for (int i = 0; i < idStr.length(); i++) {
            if (i > 0 && (idStr.length() - i) % 4 == 0) {
                result.append("-");
            }
            result.append(idStr.charAt(i));
        }

        return applicationName.toUpperCase() + "-" + result.toString();
    }

    /**
     * 从ID字符串中提取数字部分
     */
    private String extractNumberFromId(String id) {
        if (id == null || id.isEmpty()) {
            return null;
        }

        // 查找最后一个分隔符后的数字
        int lastSeparator = Math.max(id.lastIndexOf('_'), id.lastIndexOf('-'));
        if (lastSeparator >= 0 && lastSeparator < id.length() - 1) {
            String numberPart = id.substring(lastSeparator + 1);
            if (numberPart.matches("\\d+")) {
                return numberPart;
            }
        }

        return null;
    }

    // ========== Getter方法 ==========

    public boolean isInitialized() {
        return initialized.get();
    }

    // ========== 内部类定义 ==========

    /**
     * ID格式枚举
     */
    public enum IdFormat {
        /**
         * 长整型字符串: 1234567890123456789
         */
        LONG_STRING,

        /**
         * Base36编码: 9JQO58B9N
         */
        BASE36,

        /**
         * Base62编码: 1Z9c3v5N
         */
        BASE62,

        /**
         * 带前缀的长整型: APP_1234567890123456789
         */
        PREFIXED_LONG,

        /**
         * 带前缀的Base36: APP_9JQO58B9N
         */
        PREFIXED_BASE36,

        /**
         * 带前缀的Base62: APP_1Z9c3v5N
         */
        PREFIXED_BASE62,

        /**
         * 格式化显示: APP-1234-5678-9012-3456-789
         */
        FORMATTED
    }

    /**
     * ID解析信息
     */
    @Getter
    public static class IdInfo {
        private final long id;
        private final long timestamp;
        private final long workerId;
        private final long sequence;
        private final LocalDateTime dateTime;

        public IdInfo(long id, long timestamp, long workerId, long sequence) {
            this.id = id;
            this.timestamp = timestamp;
            this.workerId = workerId;
            this.sequence = sequence;
            this.dateTime = LocalDateTime.ofInstant(
                    java.time.Instant.ofEpochMilli(timestamp),
                    java.time.ZoneId.systemDefault()
            );
        }

        @Override
        public String toString() {
            return String.format(
                    "IdInfo{id=%d, timestamp=%d, workerId=%d, sequence=%d, dateTime=%s}",
                    id, timestamp, workerId, sequence, dateTime
            );
        }
    }
}